package com.hzw.saas.service.rss.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.hzw.saas.api.rss.ILibraryEsService;
import com.hzw.saas.api.rss.bo.LibraryNoticeBO;
import com.hzw.saas.api.rss.query.LibrarySearchQuery;
import com.hzw.saas.common.config.util.PageParam;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.lucene.search.TotalHits;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;

/**
 * // TODO 考虑与RssEsServiceImpl合并，重复代码较多
 * 文献检索接口实现
 * @author sonam
 * @sine 2021/9/24 5:18 下午
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class LibraryEsServiceImpl implements ILibraryEsService {

    private final String [] INDICES = { "library" };
    private final RestHighLevelClient restHighLevelClient;

    private BoolQueryBuilder searchBuilder(LibrarySearchQuery searchQuery) {
        BoolQueryBuilder boolQueryRootBuilder = QueryBuilders.boolQuery();
        // 构造指定条件
        buildCommonCondition(searchQuery, boolQueryRootBuilder);
        // 关键字条件构造
        buildKeyWordCondition(searchQuery, boolQueryRootBuilder);
        return boolQueryRootBuilder;
    }

    private void buildCommonCondition(LibrarySearchQuery searchQuery, BoolQueryBuilder boolQueryRootBuilder) {
        BoolQueryBuilder typeBuilder = QueryBuilders.boolQuery();
        if (searchQuery.getType() == 1 || searchQuery.getType() == 0){
            typeBuilder.should(QueryBuilders.termQuery("type", searchQuery.getType()));
            boolQueryRootBuilder.must(typeBuilder);
        }
        // 用户ID
        BoolQueryBuilder creatorIdsBuilder = QueryBuilders.boolQuery();
        List<String> creatorIds = searchQuery.getCreatorIds();
        if (CollectionUtil.isNotEmpty(creatorIds)) {
            creatorIds.forEach(creatorId -> {
                if (StrUtil.isBlank(creatorId)) {
                    return;
                }
                creatorIdsBuilder.should(QueryBuilders.termQuery("creatorId", creatorId));
            });
            boolQueryRootBuilder.must(creatorIdsBuilder);
        }
        // 分类编号
        BoolQueryBuilder catalogBuilder = QueryBuilders.boolQuery();
        List<String> catalogIds = searchQuery.getCatalogIds();
        if (CollectionUtil.isNotEmpty(catalogIds)) {
            catalogIds.forEach(catalogId -> {
                if (StrUtil.isBlank(catalogId)) {
                    return;
                }
                catalogBuilder.should(QueryBuilders.termQuery("treeNode", catalogId));
            });
            boolQueryRootBuilder.must(catalogBuilder);
        }
        // 期刊名称
        BoolQueryBuilder journalNamesBuilder = QueryBuilders.boolQuery();
        List<String> journalName = searchQuery.getJournalName();
        if (CollectionUtil.isNotEmpty(journalName)) {
            journalName.forEach(name -> {
                if (StrUtil.isBlank(name)) {
                    return;
                }
                journalNamesBuilder.should(QueryBuilders.matchQuery("journalName", name).operator(Operator.AND));
            });
            boolQueryRootBuilder.must(journalNamesBuilder);
        }
        // 文章标题
        BoolQueryBuilder titlesBuilder = QueryBuilders.boolQuery();
        List<String> titles = searchQuery.getTitles();
        if (CollectionUtil.isNotEmpty(titles)) {
            titles.forEach(title -> {
                if (StrUtil.isBlank(title)) {
                    return;
                }
                titlesBuilder.should(QueryBuilders.matchQuery("title", title).operator(Operator.AND));
            });
            boolQueryRootBuilder.must(titlesBuilder);
        }
        // 作者
        BoolQueryBuilder authorsBuilder = QueryBuilders.boolQuery();
        List<String> authors = searchQuery.getAuthors();
        if (CollectionUtil.isNotEmpty(authors)) {
            authors.forEach(author -> {
                if (StrUtil.isBlank(author)) {
                    return;
                }
                authorsBuilder.should(QueryBuilders.wildcardQuery("authors", author.concat("*")));
            });
            boolQueryRootBuilder.must(authorsBuilder);
        }
        // 时间段
        BoolQueryBuilder pubDateBuilder = QueryBuilders.boolQuery();
        Date pubStartDate = searchQuery.getPubStartDate();
        Date pubEndDate = searchQuery.getPubEndDate();
        if (Objects.nonNull(pubEndDate) && Objects.nonNull(pubStartDate)) {
            pubDateBuilder.should(QueryBuilders.rangeQuery("pubDate")
                .gte(DateUtil.format(pubStartDate, DatePattern.UTC_SIMPLE_PATTERN))
                .lte(DateUtil.format(pubEndDate, DatePattern.UTC_SIMPLE_PATTERN)));
            boolQueryRootBuilder.must(pubDateBuilder);
        }
    }

    private void buildKeyWordCondition(LibrarySearchQuery searchQuery, BoolQueryBuilder boolQueryRootBuilder) {
        List<String> keyWords = searchQuery.getKeyWords();
        if (CollectionUtil.isNotEmpty(keyWords)) {
            BoolQueryBuilder boolQueryOtherBuilder = QueryBuilders.boolQuery();
            keyWords.forEach(keyWord ->
                boolQueryOtherBuilder
                    .should(QueryBuilders.matchQuery("journalName", keyWord).operator(Operator.AND))
                    .should(QueryBuilders.matchQuery("title", keyWord).operator(Operator.AND))
                    .should(QueryBuilders.matchQuery("description", keyWord).operator(Operator.AND))
                    .should(QueryBuilders.wildcardQuery("authors", keyWord.concat("*")))
            );
            boolQueryRootBuilder.must(boolQueryOtherBuilder);
        }
    }

    @Override
    public Page<LibraryNoticeBO> searchLibrary(LibrarySearchQuery librarySearchQuery) {
        PageParam pageParam = librarySearchQuery.getPageParam();
        if (pageParam == null) {
            pageParam = new PageParam();
        }
        if (pageParam.getCurrent() <= 0) {
            pageParam.setCurrent(1);
        }

        // 构建检索条件
        BoolQueryBuilder boolQueryBuilder = searchBuilder(librarySearchQuery);
        // 构建es检索请求
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        sourceBuilder.query(boolQueryBuilder);
        sourceBuilder.from((int) (pageParam.getCurrent() - 1) * (int) pageParam.getSize());
        sourceBuilder.size((int) pageParam.getSize());
        SearchRequest request = new SearchRequest();
        request.source(sourceBuilder);
        request.indices(INDICES);
        SearchResponse searchResponse = null;
        try {
            searchResponse = restHighLevelClient.search(request, RequestOptions.DEFAULT);
        } catch (Exception e) {
            log.error("es检索失败", e);
        }
        SearchHits hits = searchResponse.getHits();
        TotalHits totalHits = hits.getTotalHits();
        SearchHit[] searchHits = hits.getHits();
        List<LibraryNoticeBO> libraryNotices = new ArrayList<>();
        for (SearchHit searchHit : searchHits) {
            libraryNotices.add(BeanUtil.fillBeanWithMap(searchHit.getSourceAsMap(), new LibraryNoticeBO(), true));
        }
        Page<LibraryNoticeBO> libraryNoticeBOPage = new Page<>();
        libraryNoticeBOPage.setTotal(totalHits.value);
        libraryNoticeBOPage.setSize(pageParam.getSize());
        libraryNoticeBOPage.setCurrent(pageParam.getCurrent());
        libraryNoticeBOPage.setRecords(libraryNotices);
        return libraryNoticeBOPage;
    }

}
